分享 | “三高”产品设计的这些坑,你是不是也踩过?(上)
供稿 | eBay IE Team
作者 | 许健编辑 | 顾欣怡本文9432字,预计阅读时间29分钟更多干货请关注“eBay技术荟”公众号导读
本次专栏分享,我们邀请了ebay基础架构工程部的研发总监许健,就设计高可靠、高扩展、高性能产品的主题分享他多年来的心得。共分为六个模块:稳定性/正确性、扩展性、高性能、过渡和迁移、整体考虑以及未来3-5年的挑战。此次专栏将分为上下两期,本期将着重稳定性/正确性及扩展性的探讨,其余敬请等待下期分享!
一、稳定性/正确性
01
5%的代码,95%的测试。
5% Code , 95% Test.
在 eBay 这样规模的公司做项目,我实际的感受是,50%以上的时间用于设计讨论,45%用于测试调优上线,5%用于功能性代码(大家不要纠结具体的比例数字,大致就是这个意思)。
首先,设计讨论的效率跟这个团队里面技术领导的影响力直接相关,因为大多数的时间就消耗在“谁也说不服谁”上。也正是因此,这么多年下来,我养成了辩证看问题的习惯。技术领导人独裁的时候效率最高,但是风险太大,一旦出现问题,整个团队就一起赔进去了。所以,最好还是在强有力的主导下,大家都能没有负担地表达意见。
然后再说测试调优上线,我曾经问过我们Buyer Experience 和 Payment 两大部门的主管关于交付效率提升的主要困难,他们都把测试排在首位。大概4、5 年前,eBay基础架构的领导解散了集中的QE(Quality Engineer,质量工程师)团队,把原来的测试团队打散到开发团队中去。我当时觉得这样挺好的,从测试团队加入到开发团队的同学,既可以做测试还可以帮助开发。但现在来看,这一操作或许并非百利而无一害。我们需要有人专注测试效率,但并不是给开发团队做测试,而是跟开发团队一起去开发测试框架,团队业绩则需要考量CICD效率。现在市面上也有很多CICD的开源方案: Jenkins,Spinnaker,Tekton…
02
万物皆会失效。
Everything will fail.
失效性测试非常关键,这里我举几个具体的例子,有些真的是血的教训。
我们部门出过一个28分钟的网站故障(Site Issue),导致eBay手机应用的流量直接降到零。原因是配置管理系统里的信息没有及时反映线上流量,导致我们在删除应用的时候认为该应用没有流量而做了误删除。网站大了各种妖魔鬼怪的配置都有,真是一言难尽,但是出了事故你是没有办法以这个为借口推卸责任的,弄的不好就是卷铺盖走人。
这件事情给我的教训就是,线上做删除操作时一定要擦亮眼睛,只相信设备返回的信息。这次事故以后我们但凡做删除,都会检查设备线上流量,而且先做Disable,Baking,然后再做Deletion。
我所在的是基础架构部门,网站可用性(Site Availability)就跟悬在头上的达摩克利斯之剑一样,需要时时刻刻注意。所以,我们会一直准备各种保底方案,以保证意想不到的失效发生时,也不会出大的网站故障。
年前我和Tess(eBay 基于Kubernetes 的下一代云计算平台)组的研发经理XG,网络架构师Jesse在会议室谈了2个小时,憋了以下3个保底方案:
a. IPAM(IP Allocation Manager)做IP分配必须有脱机作业(Offline Job)做IP冲突检查。
b. 网络配置DaemonSet 变更必须比对变更前后的变化,一旦有变化异常就停止变更。
像上面这样付出惨痛代价的教训有很多,但是成功的例子也不少,比如Tess(eBay 基于Kubernetes 的下一代云计算平台)团队对于API Server 做5000节点的失效性加压力测试就是一个好的案例。【详情请点击:干货 | TESS.IO在大规模集群下的性能优化】
总之,我觉得克服失效问题的关键还是思维方式和重视程度。要看你愿不愿意花大量精力,废很多脑细胞去想各种失效场景并且付诸实践。但这也只是对付你能想得到的场景,要想做到未雨绸缪,你还得多烧一些脑细胞去想想保底方案。
03
一定要设计服务降级逻辑。
Always have a degraded second path.
关于服务降级逻辑,这里展示3个真实案例。
第一个案例在当年造成了eBay长达40多分钟的网站不可用,可谓代价惨痛。当时一个叫.vip的顶级域名刚刚生效,不巧的是,eBay内部不少服务用的都是短名访问,比如访问服务A,就是 A.vip。所以在.vip顶级域名生效的时候,eBay 网站大量服务就不可用了。
我记得Sami 有一次跟我说过,他设计ZOOM(eBay商品图片存储系统)的时候就考虑过DNS 不可用,所以他在ZOOM 里面会缓存最近的DNS 解析。当 DNS 不可用时就尝试用缓存的IP。去年,总部流量管理部门的方案架构师Charles曾经给我看过一个他处理的问题:业务团队汇报当服务A 访问服务B时,每隔一段时间就会有延迟。最后查下来这个时间点其实就是本地DNS 超时缓存的时候。他后来给的建议是,添加一个脱机(Offline)的逻辑,在该时间点到达之前就刷新本地DNS 缓存,以保证服务A访问B 时不做按需 DNS 查询。这类缓存策略在跨部门服务依赖的时候也会用到。Altus(eBay 内部的应用管理产品)依赖于IT 部门的DL manager,但是自从出现过几次跟DL manager 性能有关的事故后,Altus 就开始缓存DL manager 的数据并定期刷新。可见,服务降级逻辑至关重要,这也是我们在亲身体会后的经验总结。
第二个案例发生在3年前,上海的Rheos(eBay 基于Kafka的消息中间件系统)团队一直抱怨C3(eBay Openstack)不稳定,而从C3 的角度看可用性一直是好的,十分奇怪。后来我们跟Rheos 团队坐在一起看,才发现Rheos Cluster对那些彻底死掉的VM(Virtual Machine)竟完全免疫,反而是某个活着的VM发生性能下降(很多时候是磁盘性能下降),会对Rheos整体性能产生巨大影响。
我们当时还做了统计,40%的物理机器的磁盘或多或少都有一些问题(比如坏道),但并不是Rheos 40%的机器都受到性能影响。况且,我们也没有那么多预算替换40%的机器,所以后来采取的办法是监控磁盘错误的增长速度,如果发现错误增长率在提高,我们就主动替换机器。这个功能上线以后客户体验就得到了极大提升。
第三个案例是依赖的失效问题。我们的PAAS 系统因为依赖很多,可靠性一直得不到保证。后来PAAS 团队的架构师Jonathan把PAAS的依赖做了分类,对于关键依赖,只要一点失效,PAAS 就也跟着失效,而对于非关键依赖,就看是否可以接受最终一致性,再加上刹车保底,脱机作业不停修复数据等一系列举措,可靠性终于得到了保证。
04
保证数据输入的有效性,时刻检测以预防数据损毁,帮助恢复逻辑。
Ensure input data is valid. Detect, prevent, recover from data corruption.
有一类坑我们曾经掉进去过3次,这个坑就是Sync 类问题。我们从Source Sync 数据去同步Target,结果由于各种原因从Source 读数据时发生超时,没能拿到数据。一开始还误以为是Source 的内容是空把Target 给抹掉了,实际上是因为读超时返回空,却没有正确地错误处理逻辑。
这里我要强调一下兜底原则,对于变更,最好要设置一个变更限额(Change Quota),超过限额数目的变更就应该停下来,发出告警。这里扩展一下,现在很多Cluster 都会为了高可用性(High Availability)在多个可用性区域(Availability Zone/Region)进行部署,然后做跨区域同步。但同时我们也要意识到,错误的数据和操作也会很快同步到所有的Clusters,风险极大。所以我更要强调,做变更限额,特别是跟删除有关的变更限额的重要性。
再展开讲一下前面提到的IPAM(IP Allocation Manager)对于Duplicate IP检测问题。网络组交给IPAM的可用网络段(Subnet)是不能全信的,必须做校验。就算有了校验,也还是不能尽信,因为指不定就有人绕过系统在某处起一个服务,使用IPAM 里标记可用的IP。不要说什么人家绕过系统自己起服务是不对的,世界就是这么处处充满“惊喜”,我们要做的就是无论如何,都尽量保证不出网站故障。
回顾我在基础架构部门的实际工作经验,碰到最多跟数据有关的就是CMS(Configuration Managment System,配置管理系统)和设备里的脏数据了。你要做一个操作,就要先操作设备,再操作CMS,但是因为没有事务处理(Transaction),就会留下脏数据,或者由BUG 导致留下脏数据。而清理脏数据是一个又脏又累又危险的活,这个问题到现在还是没能得到很好的解决。但没有好的解决方案不代表没有好的模式总结。
我的经验总结就是:一旦系统牵涉到数据,就要考虑定时的数据完整和有效性检查。如果要减少检查的量,那就把每一次正常服务客户的请求当成一次数据检查,也就是说给数据打上检查的时间戳,额外的检查和正常服务客户的请求都会更新这个时间戳。
这里再说两类情况。
我们监控系统的Metrics解决方案是基于开源监控系统Prometheus的,但是限制并不能很好地支持高基数(High Cardinality)的情况。那我们该怎么来检查高基数性并且做系统保护呢?现在能采取的办法是设定一个阈值,超过这个阈值就限制后续的数据。但其实这个时候影响就已经造成了,未免有些为时已晚。所以我觉得应该检测基数性增加速度,从而提前防护,或许效果会更好。事实上,淘宝搜索引擎在新上架商品搜索评分上就用了类似的方法:最近7天的销售增长速度。
05
简单即美,减少模块数量和模块间交互。
Keep it Simple. Reduce the number of components and interactions.
我在这里强烈推荐一本叫做《极简设计》介绍UI 的书。我觉得读了这本书以后可以给心里种下一颗“极简”的种子,之后做什么事情,都会努力去想,我的方案够简单吗?要有这种追求更简单的精神。还有一本书叫《精进,如何成为一个很厉害的人》,里面有一个观点就是,很多人思考问题,一找到答案就停止了,而有的人还会继续寻找更优方案,这里的更优方案,其实就是更为精简的方法。
06
尽你所能地去解耦模块。
Decouple components as much as you can.
这些都是经验所得,都有实实在在的案例。监控组架构师Huai 曾跟我说,我们分布式日志基础架构的容量(Capacity)必须每年增长60%才能满足需求。然后我们就觉得不对啊,我司业务哪有每年增长60%?后来碰到Sami,我就问他这个问题。他是这样回答我的:微服务贡献了很大一部分日志,本来一个系统,现在N个系统,日志就增加N倍。而我们公司的物品增长速度远高于买家的购买增加速度。比如你增加了30%的物品去卖,尽管交易并没有增加,但是我们公司内部的各种大数据服务都要去处理那增加的30%的物品。这些服务不仅增生了更多的日志,还要消耗计算存储网络运维资源。所以回到奥卡姆剃刀原则,代码设计的时候要用模块结构,但是不要一上来就部署一堆服务,也不要学了一点Restful,就把所有的接口都搞成 Restful。
07
高可用不是可选项,而是必须项!
HA is not optional!
高可用的必要性众所周知,真正值得探讨的是高可用的执行问题,这也是难点所在。上文我提到过,只有把Chaos Monkey 做进应用上线的流程中,才能保证其执行。在我们公司,很多服务都是通过Altus这个产品来做应用创建、上线的,那我们就把一些最基础的高可用检测做进上线流程(go live process),比如关掉若干台机器,注入一些网络延迟和丢包,弄个磁盘只读等等...
但我在这里想说的其实是另一个层面的事情,也就是我们公司的员工。一项工程要长久,除了满足公司的业务需求,还要满足员工个人的发展需求。如果就是做做上面的那些上线流程检查,对我们的员工自己的成长又有多大帮助呢?特别是我们基础架构部门的人,很多项目都是中间件,框架级别的产品,对性能和高可用的要求特别高。我觉得非常有必要把这件事花足够多的时间做透,毕竟高手都是锻炼出来的。我们需要提供这样的场景来把系统在各种情况下的问题逼出来,然后在解决的过程中去理解,去挖代码,甚至持续数周在一个问题上不断钻研。低水平的重复对我们员工的个人成长毫无意义。
而且,这样的投入对整个公司也是大有裨益的。我们基础架构部门的客户其实都是内部客户,用XFan(eBay 数据基础分析平台负责人)的话说我们的内部客户真的很好,他们没有什么要求,也不需要我们提供多少功能。就那几个很简单的功能,保证永远可用,再加快点速度就好了。
我们要有工匠精神,就把这几个功能做好。但是规模扩大后,你会发现,就这几个看似简单的功能,要做到永远可用,再加快点速度,还真不是一件容易的事情。
很多问题都是我们试出来的。如果你的系统分控制面和数据面,那就做一个实验,看把控制面关闭后数据面有没有影响。如果你的系统有自我修复能力,比如丢失心跳就会触发 Cluster 内数据再平衡(Rebalancing)或者 Kubernetes驱逐节点负载(evict node workload),那就试试心跳都丢失的情况,看会不会产生灾难性后果。
千万不要轻信开源产品,特别是当你要把开源产品投入生产环境时,一定要有“忧患意识”。你可以把生产环境的流量乘以2(Sami 说他做eBay 商品图片存储系统ZOOM 的时候乘以5),然后做压力测试,做故障模式分析。我们部门的Shone 现负责Feature Pool Software LB 解决方案的实施,他曾部署Istio以解决feature pool的问题,他的学习体会里就明确写到,不经过自己的验证的前提下,绝不能轻信开源代码。
总之,对于大环境下发生的事件要敏感一些,比如上面提到的.vip 成为顶级域名的事情,比如闰秒问题…我现在的指导原则是,如果可以,就尽量少堆一些逻辑,多做一些深层的工程设计。Rami (eBay 基础工程架构副总裁)对我的要求就是不要搞出会影响ATB(Availability to Business,企业可用性)的大型网站故障,不要把RTB(Run the Business)掉在地上,要把团队往深层工程设计实施方向转型。
08
安全问题,切忌事后诸葛亮。
Security is not an afterthought.
安全问题绝不能事后诸葛亮,大家都明白,但能真正做到的其实很少。这类问题我其实接触得并不多,一些知识还是最近一年积累的。想要了解这方面内容的,可以看一下《白帽子讲 Web安全》这本书,很有帮助。
就我所在的IE(Infrastructure Engineering)部门而言, 日常工作中主要有两类事情跟安全有关。
一类是PCI Audit,我们的不少服务都要通过其检验。我觉得很有必要去看一下 PCI DSS 的规范,否则在公司Security 团队面前很容易变成一个牵线木偶。还有一类就是IE部门推出的 Certificate,Key 和 Secrets 的生命周期管理套件,配套的还有Trust Fabric,具体的内容我们后续也会在“eBay技术荟”公众号上与大家分享交流。
09
用监控和告警实现100%的可见性。
100% visibility through monitoring and alerting.
争取用监控和告警实现100%的可见性,当然,100%很难做到。我们部门有位同学就对量化有执着的“咬文嚼字”精神。与他谈话,不要用100%,All, None这种绝对词,因为你一说他就不信;也不要跟他用Many,Less 这种非定量词,因为他会反问你:“Many? Tell me how many.”
所以这里的“100%”,估计Sami就是强调一个精神了。我们公司Payment 部门说没有监控就不能上线,我很赞同。我觉得什么事情都不能尽信,什么幺蛾子都会出现。比如,监控告警的邮件地址配错了导致收不到告警邮件,于是在系统恶化早期没有被通知到,直到系统崩溃的时候被SEC(Site Engineering Center)打Oncall电话才知道;而美国打中国的Oncall电话一接就断线甚至打不到…这一系列幺蛾子我们都经历过,都是血和泪的教训。
正在看这篇文章的同学,你们是否也遇到过这样的问题?邮箱里面收告警的文件夹有着上千封未读告警邮件,每天都在读或不读中纠结。我就有好多个这样的文件夹: IAAS Alert/ PAAS Alert/ Reparo Alert/ C3 Alert/ LBMS Alert/ UDNS Alert/ Tess Alert…根本看不下来,最后也就不看了。
狼来了的故事大家都知道,我觉得我们日常工作中碰到的问题往往不是告警太少了,而是太多了,然后真的出了个大事故说不定还没有告警,想想真是胆战心惊。
就我这些年的经验来说,我个人认为以下告警设置还是比较实用的:
1. 将系统最主要的客户用例列出来,然后写个Cron Job 不停地对着服务Call,成功率不达标就报警。
2. 记录客户的真实请求的成功率,成功率不达标就报警。
需要注意的是,整体的高成功率并不代表其中某一个客户的成功率很高,所以对于重要客户要有专门的细分。 除此之外,最好能亲自跟客户确认一下,这样设置告警是否体现客户体验。这点很重要,因为客户的需求并不一定与你的想法一致。比如我上面说的跟Rheos(eBay 基于Kafka的消息中间件)团队的交互中就发现,我在乎的VM 的Up and Down 并不是Rheos 团队最关心的,他们关心的其实是性能的稳定。3. 在集群部署的情况下,要引入异常检测,一旦集群中某个节点的关键指标和其他节点出现明显不一样,就发出告警,甚至可以直接拿掉那个节点。
拿掉节点的时候要写原因,并且有后续流程保证修复后加回Cluster。我们的C3环境曾发现很多被Disable的节点但又没写明原因,造成了不必要的浪费。二、扩展性
01
选择正确的扩展模式。
Choose the right pattern for scalability.
我觉得CAP定理还是值得学习一下的。
CAP定理,指的是在一个分布式系统中,Consistency(一致性)、 Availability(可用性)、Partition Tolerance(分区容错性),最多只能同时实现两个,三者不可兼得。
每一个方向都会有一些基础性的理论,往往就是那几篇大佬写的论文。找到这些论文,多读几遍,对今后的学习工作很有帮助。国内很多做技术的组织都叫研发中心,其实大多数哪有什么研发,只能叫工程中心,是做实施的。我们应该多读读论文,读经典的论文,培养自己读懂论文的感觉。
02
需要持久化的部分是最难做高扩展的,
就从那里入手吧!
Persistent state is the hardest to scale.
Start there!
其实何止是持久化,任何一个系统我们都需要知道瓶颈在哪里,然后从那里入手。解决了一个瓶颈就要看下一个。假设原来的瓶颈在IOPS,结果用SSD 换了Spin Disk后,CPU的问题就浮出来了,又是一个瓶颈。建议先读一遍《大规模分布式存储系统:原理解析与架构实战》扫一下盲,eBay 的上海存储组开发经理ZhiTeng 当年还建议我们去学习CRUSH算法,这是Ceph的基础。我还记得他有一句话:“高性能高可靠性存储就是给懒程序员准备的。”
其实懒程序员没有什么不好,能花点钱解决的问题不一定就需要死磕高技术的。eBay云计算进行到C3(eBay 基于Openstack的云计算平台)阶段的时候,Debashis(前eBay基础架构副总)说他在推动整个eBay 的应用变迁到云原生(Cloud Native)上也感觉到困难重重。他问我们是不是就应该投资SDN(Software Defined Network,软件定义网络),做IP移动和共享存储,有了这两个“帮手”,大部分有状态(Stateful)的问题都能转变为无状态(Stateless)的,而且对于eBay的大部分业务APP来说也足够了。
如果真的决定要用更先进的技术栈来解决现在的问题,那做性能测试必须清楚地知道性能瓶颈,压力测试的时候如果CPU、内存和IO都没有满,就继续测,测到其中一个满为止。压力测试的时候,注意压力测不上去的原因也可能是客户端不够强大。
03
了解依赖服务的亲和性,并进行相应部署。
Know your affinity to dependent services and deploy accordingly.
我觉得特别是对于性能和带宽有要求的服务,一定要重视服务所在的基础架构环境。以网络延迟和带宽为例,要清楚地知道,在同一个机架(Rack)内,跨机架和跨Bubble的区别。你得知道物理限制,比如Hadoop集群做存储计算分离时,就必须考虑网络带宽和延迟。eBay的搜索引擎对性能要求很高,基本上在做设计的时候就要考虑亲和性,比如一个查询聚合器/调度器(Query Aggregator/Dispatcher)要分发汇聚一批查询节点(Query Node)的信息,在部署的时候就要特意把查询聚合器和该聚合器负责的查询节点部署在同一个Rack内部。公司内部还大量使用ZooKeeper,监控组跟我说如果ZK Cluster跨区域(Region)部署,出现脑裂(Split Brain)的比例就会变高。
04
复杂的操作并不总能实现高扩展。
Complex operations are not always scalable.
复杂的操作并不能保证高扩展性,所以尽量不要有复杂操作。
比如eBay 的Oracle DB 上默认是不能跑存储过程的,也非常不建议搞分布式Transaction,因为这些都无法进行扩展。还是那句话,在你试图解决一个复杂问题的时候,不要一下子就沉进去,先想一想是不是可以不那么复杂,有没有更简单的方法?在公司里,一个统一的解决方案(Unified Solution)并不一定需要一个很大的集群,也可以是多集群并存。所以说,做事情先问为什么要做,再考虑怎么做。
05
预先计算,切勿繁杂。
Pre-compute and keep it simple.
做预计算就一定会牵涉到同步问题。我的经验是,同步的时候增量同步和全量刷新同步都是需要的。增量同步往往是事件触发类型的,难免有遗漏,遗漏新增数据和删除数据,这些遗漏可以靠定期的全量同步完成。全量同步的时候需多加注意,要设置一定的变更配额(Change Quota),避免把“病毒”也做大量同步。
06
考虑分区、并行和微服务。
Think in partitions, parallelism and micro-services.
分而治之,专人专项,这些在我们人类社会常识和设计系统的时候用的思想其实是一样的。关于并行,我们要考虑的是在并行的过程中是否引入过多上下文切换(Context Switch)。我们自己在工作中尚且知道要专注,不希望被打扰,程序也是一样的。对于微服务的态度,我在前面已经谈过了。
07
优先选择可扩展的算法而不是复杂、酷炫的算法!
Always choose a scalable algorithm over a cool and complex one!
eBay 总部流量管理的方案架构师Charles曾经跟我说,他纵观人类历史,凡是能够经久流传或者得到广泛应用的都是那些简单的能够以低成本复制的东西。我深以为然。Charles就像是那种学了哲学来做技术的一类人。或许,计算机科学归根结底也是一门哲学。
CRUSH 算法对我影响颇深,导致牵涉到 Hash、Mapping 的时候我都会想有没有办法用一个公式就能完成所有的Mapping 呢?一个公式才多少个字节,要是真能实现,那得多么神奇!这让我联想到生命,生命源头的受精卵就是那个最初的算法,或许这个算法只是一个非常简单的公式,但它却能让一颗细胞不断分裂复制,到成长为一个复杂构造的人,一个生命的大型分布式系统,并储存如此之多的信息。这是多么伟大而神秘的算法!就像爱因斯坦的E=mc²,极简的方程式阐述了极复杂的质能关系;就像道家的“一生二,二生三,三生万物”,极繁的根源,或许就是极简。
08
用监控和告警实现100%的可见性。
100% visibility through monitoring and alerting.
小结
本期分享主要从稳定性/正确性和扩展性这两个方面,就如何设计高可靠、高扩展、高性能产品的主题进行了总结和回顾。希望各位同学能有所收获,也欢迎大家在评论区进行友好的探讨。下期分享的主题为:高性能、过渡和迁移、整体考虑以及未来3-5年的挑战,敬请期待,不见不散!
作者简介
许健,2007年加入eBay 任搜索后端开发工程师,后从事云计算相关工作。在eBay 先后经历搜索后端架构从SPARC 到X86 转型,配置管理系统从ODB 到 CMS 转型,IAAS 和 PAAS的开发,经历eBay 云计算平台从 Stratus 到 C3 到 Tess 的过渡。现任eBay 上海基础架构工程部研发总监。
分享 | eBay流量管理之负载均衡及应用交付
案例分析 | 由Decimal操作计算引发的Spark数据丢失问题
超越“双十一”—— ebay百万TPS支付账务系统的设计与实现干货 | 起底eBay Flink的上云之路
👇点击阅读原文,一键投递